package com.rtsapp.server.logger.spi;

import com.rtsapp.server.logger.format.ILayout;
import com.rtsapp.server.logger.appender.ILoggerAppender;

/**
 * 一个具体的日志输出
 * LoggerCategory必须是线程安全的
 */
public class LoggerCategory {

    private volatile LogLevel curLevel;
    private final ThreadLocal<LogEvent> threadLocalEvents = new ThreadLocal<>();

    private final LayoutAppendersCouple[] layoutAppeenders;

    private volatile boolean isStopLog = false;
    private volatile boolean isShutdown = false;

    public LoggerCategory( LogLevel curLevel, LayoutAppendersCouple[] layoutAppeenders ) {
        this.curLevel = curLevel;
        this.layoutAppeenders = layoutAppeenders;
    }

    public void setLevel( LogLevel level ){
        this.curLevel = level;
    }


    boolean isEnabled( LogLevel level ){
        return level.isGreaterOrEqual( curLevel );
    }

    void stopLog( ){
        isStopLog = true;
    }

    void shutdown(){
        isShutdown = true;

        for( LayoutAppendersCouple  couple : layoutAppeenders ){
            for( ILoggerAppender appender : couple.appenders ){
                appender.close();
            }
        }
    }

    public void log( String logerClassName,  LogLevel level,  Throwable e, String logFomat, Object...params ){

        if( isShutdown ||  isStopLog ){
            LogLog.error( "日志接口已经关闭" );
            return;
        }

        if(  level.isLessThan(curLevel) ){
            return;
        }

        LogEvent event = getEvent(logerClassName, level, Thread.currentThread(), e, logFomat, params );

        for( LayoutAppendersCouple couple : layoutAppeenders ){
            couple.log( event );
        }

    }

    private LogEvent getEvent( String logerClassName, LogLevel level, Thread thread,  Throwable e, String logFomat, Object...params  ){

        LogEvent event =  threadLocalEvents.get();
        if( event == null ){
            event = new LogEvent();
            threadLocalEvents.set( event );
        }
        event.set( logerClassName,  thread, level, e , logFomat, params );

        return event;
    }


    /**
     * 一个Category中, 同一格式的几个
     *
     */
    public static  final class  LayoutAppendersCouple{

        private final LogLevel minThreshold;
        /**
         * 格式解析类
         */
        private final ILayout layout;

        /**
         * 日志追加
         */
        private final ILoggerAppender[] appenders;
        /**
         * 关心的日志级别
         */
        private final LogLevel[] thresholds;

        /**
         * 线程Local
         */
        private final ThreadLocal<StringBuilder> threadLocalBuffers = new ThreadLocal<>();



        public LayoutAppendersCouple( ILayout layout, ILoggerAppender[] appenders, LogLevel[] thresholds ) {
            this.layout = layout;
            this.appenders = appenders;
            this.thresholds = thresholds;

            LogLevel minTresh = LogLevel.ALL;
            for( int i = 0; i < thresholds.length; i++ ){
                if( thresholds[i].isLessThan(minTresh) ){
                    minTresh = thresholds[i];
                }
            }
            minThreshold = minTresh;
        }


        public  void log( LogEvent event ){

            if( event.getLevel().isLessThan(minThreshold) ){
                return;
            }

            // 格式化日志一次
            StringBuilder sb = getBuffer();
            layout.formatLog(sb, event);
            String logString = sb.toString();

            // 记录到所有的appender中
            for( int i = 0; i < appenders.length; i++ ){
                if( event.getLevel().isLessThan(thresholds[i]) ){
                    continue;
                }

                appenders[ i ].appendLog( logString );
            }
        }

        private StringBuilder getBuffer(){
            StringBuilder sb = threadLocalBuffers.get();
            if( sb == null ){
                sb = new StringBuilder( );
                threadLocalBuffers.set( sb );
            }
            sb.setLength(0);
            return sb;
        }

    }
    
}
